﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XCommunity.Data
{
  public static class PagingExtension
  {


    /// <summary>
    /// 从序列的指定位置返回指定数量的连续元素
    /// </summary>
    /// <typeparam name="TSource">list 中的元素的类型</typeparam>
    /// <param name="list">要从其返回元素的序列</param>
    /// <param name="start">要返回的元素在序列的开始位置</param>
    /// <param name="count">要返回的元素数量</param>
    /// <returns></returns>
    public static IEnumerable<TSource> Take<TSource>( this IList<TSource> list, int start, int count )
    {

      for ( int index = start; index < Math.Min( start + count, list.Count ); index++ )
      {
        yield return list[index];
      }
    }

    /// <summary>
    /// 从数组中拷贝出一个片段
    /// </summary>
    /// <typeparam name="T">数组元素类型</typeparam>
    /// <param name="source">要从其拷贝的源数组</param>
    /// <param name="start">片段的开始位置</param>
    /// <param name="size">片段的大小</param>
    /// <returns></returns>
    public static T[] CopySegment<T>( this T[] source, int start, int size )
    {
      T[] result = new T[size];
      Array.Copy( source, start, result, 0, size );

      return result;
    }


    /// <summary>
    /// 获取分页器的最大页数
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="pagingData"></param>
    /// <returns></returns>
    public static int GetMaxPage<T>( this IPagingData<T> pagingData )
    {
      int count = pagingData.Count();
      int pageSize = pagingData.PageSize;

      return count / pageSize + ( ( count % pageSize ) == 0 ? 0 : 1 );
    }


    /// <summary>
    /// 从IList&lt;T&gt;创建一个IPagingDataSource&lt;T&gt;。
    /// </summary>
    /// <typeparam name="T">source 中的元素的类型</typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static IPagingDataSource<T> ToPagingSource<T>( this IList<T> source )
    {
      return new PagingSource<T>( source );
    }


    /// <summary>
    /// 从IEnumerable&lt;T&gt;创建一个IPagingDataSource&lt;T&gt;。
    /// </summary>
    /// <typeparam name="T">source 中的元素的类型</typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static IPagingDataSource<T> ToPagingSource<T>( this IEnumerable<T> source )
    {
      return new PagingSource<T>( source );
    }



    public static IPagingData<TResult> Select<TSource, TResult>( this IPagingData<TSource> source, Func<TSource, TResult> selector )
    {
      return new PagingSelector<TSource, TResult>( source, selector );
    }


    public static IPagingDataSource<TResult> Select<TSource, TResult>( this IPagingDataSource<TSource> source, Func<TSource, TResult> selector )
    {
      return new PagingSourceSelector<TSource, TResult>( source, selector );
    }


    private class PagingSource<T> : IPagingDataSource<T>
    {
      private IList<T> _listSource;

      private IEnumerable<T> _enumerableSource;

      public PagingSource( IList<T> source )
      {
        _listSource = source;
      }

      public PagingSource( IEnumerable<T> source )
      {
        _enumerableSource = source;
        _listSource = source as IList<T>;
      }

      #region IPagingDataSource<T> 成员

      public IPagingData<T> CreatePaging( int pageSize )
      {

        if ( _listSource != null )
          return new ListPaging<T>( _listSource, pageSize );
        else
          return new EnumerablePaging<T>( _enumerableSource, pageSize );
      }

      #endregion
    }

    private class ListPaging<T> : IPagingData<T>
    {

      private IList<T> _dataSource;
      private int _pageSize;


      public ListPaging( IList<T> source, int pageSize )
      {
        _dataSource = source;
        _pageSize = pageSize;
      }

      public IEnumerable<T> GetPage( int pageIndex )
      {
        return _dataSource.Take( ( pageIndex - 1 ) * PageSize, PageSize );
      }

      public int Count()
      {
        return _dataSource.Count;
      }

      public int PageSize
      {
        get { return _pageSize; }
      }
    }


    private class EnumerablePaging<T> : IPagingData<T>
    {

      private IEnumerable<T> _dataSource;
      private int _pageSize;


      public EnumerablePaging( IEnumerable<T> source, int pageSize )
      {
        _dataSource = source;
        _pageSize = pageSize;
      }

      public IEnumerable<T> GetPage( int pageIndex )
      {
        return _dataSource.Take( pageIndex * PageSize ).Skip( ( pageIndex - 1 ) * PageSize );
      }

      public int Count()
      {
        return _dataSource.Count();
      }

      public int PageSize
      {
        get { return _pageSize; }
      }
    }

    private class PagingSelector<TSource, TResult> : IPagingData<TResult>
    {

      private Func<TSource, TResult> _selector;
      IPagingData<TSource> _dataSource;

      public PagingSelector( IPagingData<TSource> source, Func<TSource, TResult> selector )
      {
        _dataSource = source;
        _selector = selector;
      }


      #region IPagingData<TResult> 成员

      public IEnumerable<TResult> GetPage( int pageIndex )
      {
        return _dataSource.GetPage( pageIndex ).Select( _selector );
      }

      public int Count()
      {
        return _dataSource.Count();
      }

      public int PageSize
      {
        get { return _dataSource.PageSize; }
      }

      #endregion
    }

    private class PagingSourceSelector<TSource, TResult> : IPagingDataSource<TResult>
    {

      private Func<TSource, TResult> _selector;
      IPagingDataSource<TSource> _dataSource;

      public PagingSourceSelector( IPagingDataSource<TSource> source, Func<TSource, TResult> selector )
      {
        _dataSource = source;
        _selector = selector;
      }


      #region IPagingDataSource<TResult> 成员

      public IPagingData<TResult> CreatePaging( int pageSize )
      {
        return _dataSource.CreatePaging( pageSize ).Select( _selector );
      }

      #endregion
    }

  }
}
